home *** CD-ROM | disk | FTP | other *** search
/ Clickx 115 / Clickx 115.iso / software / tools / windows / tails-i386-0.16.iso / live / filesystem.squashfs / usr / share / pyshared / liveusb / gui.py < prev    next >
Encoding:
Python Source  |  2012-04-13  |  30.2 KB  |  752 lines

  1. # -*- coding: utf-8 -*-
  2. #
  3. # Copyright ┬⌐ 2008-2010  Red Hat, Inc. All rights reserved.
  4. # Copyright ┬⌐ 2008-2010  Luke Macken <lmacken@redhat.com>
  5. # Copyright ┬⌐ 2008  Kushal Das <kushal@fedoraproject.org>
  6. #
  7. # This copyrighted material is made available to anyone wishing to use, modify,
  8. # copy, or redistribute it subject to the terms and conditions of the GNU
  9. # General Public License v.2.  This program is distributed in the hope that it
  10. # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the
  11. # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  12. # See the GNU General Public License for more details.  You should have
  13. # received a copy of the GNU General Public License along with this program; if
  14. # not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
  15. # Floor, Boston, MA 02110-1301, USA. Any Red Hat trademarks that are
  16. # incorporated in the source code or documentation are not subject to the GNU
  17. # General Public License and may only be used or replicated with the express
  18. # permission of Red Hat, Inc.
  19. #
  20. # Author(s): Luke Macken <lmacken@redhat.com>
  21. #            Kushal Das <kushal@fedoraproject.org>
  22.  
  23. """
  24. A cross-platform graphical interface for the LiveUSBCreator
  25. """
  26.  
  27. import os
  28. import sys
  29. import logging
  30. import urlparse
  31.  
  32. from time import sleep
  33. from datetime import datetime
  34. from PyQt4 import QtCore, QtGui
  35.  
  36. from liveusb import LiveUSBCreator, LiveUSBError, LiveUSBInterface, _
  37. from liveusb.config import config
  38. from liveusb.source import SourceError, LocalIsoSource
  39. from liveusb.source import RunningLiveSystemSource
  40. from liveusb.releases import releases
  41. from liveusb.utils import _to_unicode
  42. if sys.platform == 'win32':
  43.     from liveusb.urlgrabber.grabber import URLGrabber, URLGrabError
  44.     from liveusb.urlgrabber.progress import BaseMeter
  45. else:
  46.     from urlgrabber.grabber import URLGrabber, URLGrabError
  47.     from urlgrabber.progress import BaseMeter
  48.  
  49. try:
  50.     import dbus.mainloop.qt
  51.     dbus.mainloop.qt.DBusQtMainLoop(set_as_default=True)
  52. except:
  53.     pass
  54.  
  55.  
  56. class LiveUSBApp(QtGui.QApplication):
  57.     """ Main application class """
  58.     def __init__(self, opts, args):
  59.         QtGui.QApplication.__init__(self, args) 
  60.         self.mywindow = LiveUSBDialog(opts, args)
  61.         self.mywindow.show()
  62.         try:
  63.             self.exec_()
  64.         finally:
  65.             self.mywindow.terminate()
  66.  
  67.  
  68. class ReleaseDownloader(QtCore.QThread):
  69.  
  70.     def __init__(self, release, progress, proxies):
  71.         QtCore.QThread.__init__(self)
  72.         self.release = release
  73.         self.progress = progress
  74.         self.proxies = proxies
  75.         for rel in releases:
  76.             if rel['name'] == str(release):
  77.                 self.url = rel['url']
  78.                 break
  79.         else:
  80.             raise LiveUSBError(_("Unknown release: %s") % release)
  81.  
  82.     def run(self):
  83.         self.emit(QtCore.SIGNAL("status(PyQt_PyObject)"),
  84.                   _("Downloading %s...") % os.path.basename(self.url))
  85.         grabber = URLGrabber(progress_obj=self.progress, proxies=self.proxies)
  86.         home = os.getenv('HOME', 'USERPROFILE')
  87.         filename = os.path.basename(urlparse.urlparse(self.url).path)
  88.         for folder in ('Downloads', 'My Documents'):
  89.             if os.path.isdir(os.path.join(home, folder)):
  90.                 filename = os.path.join(home, folder, filename)
  91.                 break
  92.         try:
  93.             iso = grabber.urlgrab(self.url, reget='simple')
  94.         except URLGrabError, e:
  95.             self.emit(QtCore.SIGNAL("dlcomplete(PyQt_PyObject)"), e.strerror)
  96.         else:
  97.             self.emit(QtCore.SIGNAL("dlcomplete(PyQt_PyObject)"), iso)
  98.  
  99.  
  100. class DownloadProgress(QtCore.QObject, BaseMeter):
  101.     """ A QObject urlgrabber BaseMeter class.
  102.  
  103.     This class is called automatically by urlgrabber with our download details.
  104.     This class then sends signals to our main dialog window to update the
  105.     progress bar.
  106.     """
  107.     def start(self, filename=None, url=None, basename=None, size=None,
  108.               now=None, text=None):
  109.         self.emit(QtCore.SIGNAL("maxprogress(int)"), size)
  110.  
  111.     def update(self, amount_read, now=None):
  112.         """ Update our download progressbar.
  113.  
  114.         :read: the number of bytes read so far
  115.         """
  116.         self.emit(QtCore.SIGNAL("progress(int)"), amount_read)
  117.  
  118.     def end(self, amount_read):
  119.         self.update(amount_read)
  120.  
  121.  
  122. class ProgressThread(QtCore.QThread):
  123.     """ A thread that monitors the progress of Live USB creation.
  124.  
  125.     This thread periodically checks the amount of free space left on the 
  126.     given drive and sends a signal to our main dialog window to update the
  127.     progress bar.
  128.     """
  129.     totalsize = 0
  130.     orig_free = 0
  131.     drive = None
  132.     get_free_bytes = None
  133.  
  134.     def set_data(self, size, drive, freebytes):
  135.         self.totalsize = size / 1024
  136.         self.drive = drive
  137.         self.get_free_bytes = freebytes
  138.         self.orig_free = self.get_free_bytes()
  139.         self.emit(QtCore.SIGNAL("maxprogress(int)"), self.totalsize)
  140.  
  141.     def run(self):
  142.         while True:
  143.             free = self.get_free_bytes()
  144.             if free is None:
  145.                 break
  146.             value = (self.orig_free - free) / 1024
  147.             self.emit(QtCore.SIGNAL("progress(int)"), value)
  148.             if value >= self.totalsize:
  149.                 break
  150.             sleep(3)
  151.  
  152.     def terminate(self):
  153.         self.emit(QtCore.SIGNAL("progress(int)"), self.totalsize)
  154.         QtCore.QThread.terminate(self)
  155.  
  156.  
  157. class LiveUSBThread(QtCore.QThread):
  158.  
  159.     def __init__(self, live, progress, parent=None):
  160.         QtCore.QThread.__init__(self, parent)
  161.         self.progress = progress
  162.         self.parent = parent
  163.         self.live = live
  164.  
  165.     def status(self, text):
  166.         self.emit(QtCore.SIGNAL("status(PyQt_PyObject)"), text)
  167.  
  168.     def rescan_devices(self, force_partitions=False):
  169.         self._waiting_detection = True
  170.  
  171.         def detection_done():
  172.             self._waiting_detection = False
  173.  
  174.         self.live.detect_removable_drives(
  175.             callback=detection_done,
  176.             force_partitions=force_partitions)
  177.  
  178.         while self._waiting_detection:
  179.             self.sleep(1)
  180.  
  181.     def installation_complete(self):
  182.         self.emit(QtCore.SIGNAL("done()"))
  183.  
  184.     def run(self):
  185.         self.handler = LiveUSBLogHandler(self.status)
  186.         self.live.log.addHandler(self.handler)
  187.         self.now = datetime.now()
  188.         try:
  189.             if self.parent.opts.partition:
  190.                 self.live.unmount_device()
  191.                 if not self.live.can_read_partition_table():
  192.                     self.live.log.info('Clearing unreadable partition table.')
  193.                     self.live.clear_all_partition_tables()
  194.                 if not self.live.is_already_partitioned():
  195.                     if not self.live.is_device_big_enough():
  196.                         raise LiveUSBError(
  197.                             _("Device is too small: it must be at least %s MiB.")
  198.                               % self.live.system_partition_size)
  199.                     self.live.partition_device()
  200.                 else:
  201.                     self.live.log.info('Device already partitioned!')
  202.                 self.live.create_hybrid_mbr()
  203.                 self.rescan_devices(force_partitions=True)
  204.                 self.live.switch_drive_to_system_partition()
  205.                 self.live.format_device()
  206.                 self.live.mount_device()
  207.  
  208.             #if self.parent.opts.format:
  209.             #    self.live.unmount_device()
  210.             #    self.live.format_device()
  211.  
  212.             # Initialize zip-drive-compatible geometry
  213.             #if self.parent.opts.zip:
  214.             #    self.live.dest = self.live.drive['mount']
  215.             #    self.live.drive['unmount'] = True
  216.             #    self.live.unmount_device()
  217.             #    self.live.initialize_zip_geometry()
  218.             #    self.live.drive = self.parent.get_selected_drive()
  219.             #    self.live.dest = self.live.drive['mount']
  220.             #    self.live.drive['unmount'] = True
  221.             #    self.live.unmount_device()
  222.             #    self.live.format_device()
  223.  
  224.             self.live.verify_filesystem()
  225.             if not self.live.drive['uuid'] and not self.live.label:
  226.                 self.status(_("Error: Cannot set the label or obtain " 
  227.                               "the UUID of your device.  Unable to continue."))
  228.                 self.live.log.removeHandler(self.handler)
  229.                 return
  230.  
  231.             self.live.check_free_space()
  232.  
  233.             if not self.parent.opts.noverify:
  234.                 # Verify the MD5 checksum inside of the ISO image
  235.                 if not self.live.verify_iso_md5():
  236.                     self.live.log.removeHandler(self.handler)
  237.                     return
  238.  
  239.                 # If we know about this ISO, and it's SHA1 -- verify it
  240.                 release = self.live.source.get_release()
  241.                 if release and ('sha1' in release or 'sha256' in release):
  242.                     if not self.live.verify_iso_sha1(progress=self):
  243.                         self.live.log.removeHandler(self.handler)
  244.                         return
  245.  
  246.             # Setup the progress bar
  247.             self.progress.set_data(size=self.live.totalsize,
  248.                                    drive=self.live.drive['device'],
  249.                                    freebytes=self.live.get_free_bytes)
  250.             self.progress.start()
  251.  
  252.             self.live.extract_iso()
  253.             self.live.create_persistent_overlay()
  254.             self.live.update_configs()
  255.  
  256.             if self.parent.opts.partition:
  257.                 self.live.reset_mbr()
  258.             self.live.install_bootloader()
  259.             # self.live.bootable_partition()
  260.  
  261.             if self.parent.opts.device_checksum:
  262.                 self.live.calculate_device_checksum(progress=self)
  263.             if self.parent.opts.liveos_checksum:
  264.                 self.live.calculate_liveos_checksum()
  265.  
  266.             # Flush all filesystem buffers and unmount
  267.             self.live.flush_buffers()
  268.             self.live.unmount_device()
  269.  
  270.             if self.parent.opts.partition:
  271.                 self.rescan_devices()
  272.                 self.live.switch_back_to_full_drive()
  273.                 self.live.remove_hybrid_mbr()
  274.  
  275.             duration = str(datetime.now() - self.now).split('.')[0]
  276.             self.status(_("Installation complete! (%s)") % duration)
  277.             self.installation_complete()
  278.  
  279.         except Exception, e:
  280.             self.status(e.args[0])
  281.             self.status(_("LiveUSB creation failed!"))
  282.             self.live.log.exception(e)
  283.  
  284.         self.live.log.removeHandler(self.handler)
  285.         self.progress.terminate()
  286.  
  287.     def set_max_progress(self, maximum):
  288.         self.emit(QtCore.SIGNAL("maxprogress(int)"), maximum)
  289.  
  290.     def update_progress(self, value):
  291.         self.emit(QtCore.SIGNAL("progress(int)"), value)
  292.  
  293.     def __del__(self):
  294.         self.wait()
  295.  
  296.  
  297. class LiveUSBLogHandler(logging.Handler):
  298.  
  299.     def __init__(self, cb):
  300.         logging.Handler.__init__(self)
  301.         self.cb = cb
  302.  
  303.     def emit(self, record):
  304.         if record.levelname in ('INFO', 'ERROR'):
  305.             self.cb(record.msg)
  306.  
  307.  
  308. class LiveUSBDialog(QtGui.QDialog, LiveUSBInterface):
  309.     """ Our main dialog class """
  310.  
  311.     def __init__(self, opts, args):
  312.         self.in_process = False
  313.         QtGui.QDialog.__init__(self)
  314.         LiveUSBInterface.__init__(self)
  315.         self.opts = opts
  316.         self.args = args
  317.         self.signals_connected = []
  318.         self.setupUi(self)
  319.         self.titleLabel.setPixmap(QtGui.QPixmap(config['branding']['header']))
  320.         self.source_available = False
  321.         self.target_available = False
  322.         self.target_selected  = False
  323.         self.persistence = False
  324.         self.update_start_button()
  325.         self.startButton.setIcon(QtGui.QIcon.fromTheme("go-next"))
  326.         if self.opts.clone or config['download']['enabled']:
  327.             self.source_available = True
  328.         if self.opts.clone:
  329.             self.sourceFileGroupBox.setVisible(False)
  330.             self.orLabel.setVisible(False)
  331.             self.downloadGroupBox.setVisible(False)
  332.         if not config['download']['enabled']:
  333.             self.orLabel.setVisible(False)
  334.             self.downloadGroupBox.setVisible(False)
  335.         if sys.platform == 'win32':
  336.             self.driveBox.setGeometry(QtCore.QRect(10, 20, 145, 22))
  337.             self.refreshDevicesButton = QtGui.QPushButton(self.targetGroupBox)
  338.             self.refreshDevicesButton.setGeometry(QtCore.QRect(156, 16, 30, 26))
  339.             self.refreshDevicesButton.setText("")
  340.             icon = QtGui.QIcon()
  341.             icon.addPixmap(QtGui.QPixmap(":/refresh.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
  342.             self.refreshDevicesButton.setIcon(icon)
  343.             self.refreshDevicesButton.setFlat(True)
  344.             self.refreshDevicesButton.setObjectName("refreshDevicesButton")
  345.         self.update_start_button()
  346.         self.live = LiveUSBCreator(opts=opts)
  347.         if self.opts.clone:
  348.             self.live.source = RunningLiveSystemSource(
  349.                 path=config['running_liveos_mountpoint'])
  350.         if self.opts.partition and config['persistence']['enabled']:
  351.             self.overlayTitle.setVisible(False)
  352.             # FIXME: Get the persistence checkbox back.
  353.             # self.persistenceTitle.setVisible(True)
  354.         elif config['persistence']['enabled']:
  355.             self.overlayTitle.setVisible(True)
  356.             # self.persistenceTitle.setVisible(False)
  357.         else:
  358.             self.overlayTitle.setVisible(False)
  359.             # self.persistenceTitle.setVisible(False)
  360.         if not self.opts.clone:
  361.             self.populate_releases()
  362.         self.populate_devices()
  363.         self.downloader = None
  364.         self.progress_thread = ProgressThread()
  365.         self.download_progress = DownloadProgress()
  366.         self.live_thread = LiveUSBThread(live=self.live,
  367.                                          progress=self.progress_thread,
  368.                                          parent=self)
  369.         self.connect_slots()
  370.         self.confirmed = False
  371.         self.delete_existing_liveos_confirmed = False
  372.         self.mbr_reset_confirmed = False
  373.  
  374.         # Intercept all liveusb INFO log messages, and display them in the gui
  375.         self.handler = LiveUSBLogHandler(lambda x: self.textEdit.append(x))
  376.         self.live.log.addHandler(self.handler)
  377.         if not self.opts.verbose:
  378.             self.live.log.removeHandler(self.live.handler)
  379.  
  380.         # If an ISO was specified on the command line, use it.
  381.         if args:
  382.             for arg in self.args:
  383.                 if arg.lower().endswith('.iso') and os.path.exists(arg):
  384.                     self.selectfile(arg)
  385.  
  386.         # Determine if we have admin rights
  387.         if not self.opts.unprivileged and not self.live.is_admin():
  388.             self.live.log.error(_('Warning: This tool needs to be run as an '
  389.                 'Administrator. To do this, right click on the icon and open '
  390.                 'the Properties. Under the Compatibility tab, check the "Run '
  391.                 'this program as an administrator" box.'))
  392.  
  393.     def set_persistence(self):
  394.         # FIXME: get persistencecheckbox back
  395.         # if self.persistencecheckBox.isChecked():
  396.         #     self.persistence = True
  397.         # else:
  398.         #     self.persistence = False
  399.         self.persistence = False
  400.  
  401.     def update_start_button(self):
  402.         if self.source_available and self.target_available:
  403.             self.startButton.setEnabled(True)
  404.         else:
  405.             self.startButton.setEnabled(False)
  406.  
  407.     def populate_devices(self, *args, **kw):
  408.         if self.in_process or self.target_selected:
  409.             return
  410.         def add_devices():
  411.             self.driveBox.clear()
  412.             if not len(self.live.drives):
  413.                 self.textEdit.setPlainText(_("Unable to find any USB drive"))
  414.                 self.target_available = False
  415.                 self.update_start_button()
  416.                 return
  417.             self.live.log.debug('drives: %s' % self.live.drives)
  418.             for device, info in self.live.drives.items():
  419.                 # Skip the device that is the source of the copy
  420.                 if self.live.source and self.live.source.dev and (info['udi'] == self.live.source.dev or info['parent_udi'] == self.live.source.dev):
  421.                     self.live.log.debug('Skipping source device: %s' % info['device'])
  422.                     continue
  423.                 # Skip the running device
  424.                 if self.live.running_device() in [info['udi'], info['parent_udi']]:
  425.                     self.live.log.debug('Skipping running device: %s' % info['device'])
  426.                     continue
  427.                 # Skip LUKS-encrypted partitions
  428.                 if info['fstype'] and info['fstype'] == 'crypto_LUKS':
  429.                     self.live.log.debug('Skipping LUKS-encrypted partition: %s' % info['device'])
  430.                     continue
  431.                 if info['label']:
  432.                     details = info['label']
  433.                 else:
  434.                     details = '%0.1f GB' % (info['size'] / 10.0**9)
  435.                 self.driveBox.addItem("%s (%s)" % (device, details))
  436.             self.target_available = True
  437.             self.update_start_button()
  438.  
  439.         try:
  440.             self.live.detect_removable_drives(callback=add_devices)
  441.         except LiveUSBError, e:
  442.             self.textEdit.setPlainText(e.args[0])
  443.             self.target_available = False
  444.             self.update_start_button()
  445.  
  446.     def populate_releases(self):
  447.         for release in [release['name'] for release in releases]:
  448.             self.downloadCombo.addItem(release)
  449.  
  450.     def connect_slots(self):
  451.         self.connect(self.isoBttn, QtCore.SIGNAL("clicked()"), self.selectfile)
  452.         self.connect(self.startButton, QtCore.SIGNAL("clicked()"), self.begin)
  453.         self.connect(self.overlaySlider, QtCore.SIGNAL("valueChanged(int)"),
  454.                      self.overlay_value)
  455.         # FIXME: get persistencecheckbox back
  456.         # self.connect(self.persistencecheckBox, QtCore.SIGNAL("clicked()"), self.set_persistence)
  457.         self.connect(self.live_thread, QtCore.SIGNAL("status(PyQt_PyObject)"),
  458.                      self.status)
  459.         self.connect(self.live_thread, QtCore.SIGNAL("finished()"),
  460.                      lambda: self.enable_widgets(True))
  461.         self.connect(self.live_thread, QtCore.SIGNAL("terminated()"),
  462.                      lambda: self.enable_widgets(True))
  463.         self.connect(self.live_thread, QtCore.SIGNAL("progress(int)"),
  464.                      self.progress)
  465.         self.connect(self.live_thread, QtCore.SIGNAL("maxprogress(int)"),
  466.                      self.maxprogress)
  467.         self.connect(self.progress_thread, QtCore.SIGNAL("progress(int)"),
  468.                      self.progress)
  469.         self.connect(self.progress_thread, QtCore.SIGNAL("maxprogress(int)"),
  470.                      self.maxprogress)
  471.         self.connect(self.download_progress, QtCore.SIGNAL("maxprogress(int)"),
  472.                      self.maxprogress)
  473.         self.connect(self.download_progress, QtCore.SIGNAL("progress(int)"),
  474.                      self.progress)
  475.         self.connect(self.live_thread, QtCore.SIGNAL("done()"),
  476.                      self.show_end_dialog)
  477.         if hasattr(self, 'refreshDevicesButton'):
  478.             self.connect(self.refreshDevicesButton, QtCore.SIGNAL("clicked()"),
  479.                          self.populate_devices)
  480.  
  481.         # If we have access to HAL & DBus, intercept some useful signals
  482.         if hasattr(self.live, 'udisks'):
  483.             self.signals_connected += [
  484.                 self.live.udisks.connect_to_signal('DeviceAdded',
  485.                                                    self.populate_devices),
  486.                 self.live.udisks.connect_to_signal('DeviceRemoved',
  487.                                                    self.populate_devices),
  488.                 self.live.udisks.connect_to_signal('DeviceChanged',
  489.                                                    self.populate_devices)
  490.                 ]
  491.  
  492.     @QtCore.pyqtSignature("QString")
  493.     def on_driveBox_currentIndexChanged(self, drive):
  494.         """ Change the maximum overlay size when each drive is selected.
  495.  
  496.         This sets the maximum megabyte size of the persistent storage slider
  497.         to the number of free megabytes on the currently selected
  498.         "Target Device".  If the device is not mounted, or if it has more than
  499.         2gigs of free space, set the maximum to 2047mb, which is apparently
  500.         the largest file we can/should store on a vfat partition.
  501.         """
  502.         if not str(drive):
  503.             return
  504.         self._refresh_overlay_slider(str(drive).split()[0])
  505.  
  506.     def _refresh_overlay_slider(self, drive=None):
  507.         """
  508.         Reset the persistent storage slider based on the amount of free space
  509.         on the device and the ISO size.
  510.         """
  511.         if not drive:
  512.             drive = self.get_selected_drive()
  513.             if not drive:
  514.                 return
  515.  
  516.         device = self.live.drives[drive]
  517.         freespace = device['free']
  518.         current_overlay = self.overlaySlider.value()
  519.  
  520.         if not device['mount']:
  521.             self.live.log.warning(_('Device is not yet mounted, so we cannot '
  522.                                     'determine the amount of free space.  '
  523.                                     'Setting a maximum limit of 8G for the '
  524.                                     'persistent storage.'))
  525.             freespace = 8192
  526.         else:
  527.             if not freespace:
  528.                 self.live.log.warning(_('No free space on device %(device)s') % {'device': drive})
  529.                 freespace = 0
  530.  
  531.         # FAT16 cannot handle files greater than 2G
  532.         if device['fsversion'] == 'FAT16':
  533.             self.live.log.warning(_('Partition is FAT16; Restricting overlay '
  534.                                     'size to 2G'))
  535.             if freespace > 2047:
  536.                 freespace = 2047
  537.  
  538.         # Subtract the size of the ISO from our maximum overlay size
  539.         if self.live.source and self.live.source.size:
  540.             freespace -= self.live.source.size / 1024**2
  541.  
  542.         freespace -= 1 # Don't fill the device 100%
  543.  
  544.         if freespace < 0:
  545.             freespace = 0
  546.         if freespace < current_overlay:
  547.             self.overlaySlider.setValue(freespace)
  548.             self.live.overlay = self.overlaySlider.value()
  549.  
  550.         self.overlaySlider.setMaximum(freespace)
  551.  
  552.     def progress(self, value):
  553.         self.progressBar.setValue(value)
  554.  
  555.     def maxprogress(self, value):
  556.         self.progressBar.setMaximum(value)
  557.  
  558.     def status(self, text):
  559.         if isinstance(text, Exception):
  560.             text = text.args[0]
  561.         self.textEdit.append(text)
  562.  
  563.     def enable_widgets(self, enabled=True):
  564.         if enabled:
  565.             self.update_start_button()
  566.         else:
  567.             self.startButton.setEnabled(False)
  568.         self.driveBox.setEnabled(enabled and not self.target_selected)
  569.         self.overlaySlider.setEnabled(enabled)
  570.         self.isoBttn.setEnabled(enabled)
  571.         self.downloadCombo.setEnabled(enabled)
  572.         if hasattr(self, 'refreshDevicesButton'):
  573.             self.refreshDevicesButton.setEnabled(enabled)
  574.         self.in_process = not enabled
  575.  
  576.     def overlay_value(self, value):
  577.         self.overlayTitle.setTitle(_("Persistent Storage") + " (%d MB)" % value)
  578.  
  579.     def get_selected_drive(self):
  580.         text = _to_unicode(self.driveBox.currentText()).split()
  581.         if text:
  582.             return text[0]
  583.  
  584.     def show_end_dialog(self):
  585.         reply = QtGui.QMessageBox.information(self,
  586.                                   _("Installation complete!"),
  587.                                   _("Installation was completed. Press OK to close this program."),
  588.                                    QtGui.QMessageBox.Ok)
  589.         if reply == QtGui.QMessageBox.Ok:
  590.             self.close()
  591.  
  592.     def begin(self):
  593.         """ Begin the liveusb creation process.
  594.  
  595.         This method is called when the "Create Live USB" button is clicked.
  596.         """
  597.         self.enable_widgets(False)
  598.         self.live.overlay = self.overlaySlider.value()
  599.         if not self.target_selected:
  600.             self.live.drive = self.get_selected_drive()
  601.         self.target_selected = True
  602.         for signal_match in self.signals_connected:
  603.             signal_match.remove()
  604.  
  605.         # Unmount the device and check the MBR
  606.         if self.live.blank_mbr():
  607.             if self.opts.partition:
  608.                 self.mbr_reset_confirmed = True
  609.             if not self.mbr_reset_confirmed:
  610.                 self.status(_("The Master Boot Record on your device is blank. "
  611.                               "Pressing 'Create Live USB' again will reset the "
  612.                               "MBR on this device."))
  613.                 self.mbr_reset_confirmed = True
  614.                 self.enable_widgets(True)
  615.                 return
  616.             if self.live.drive['mount']:
  617.                 self.live.dest = self.live.drive['mount']
  618.                 self.live.unmount_device()
  619.             self.live.reset_mbr()
  620.         elif not self.live.mbr_matches_syslinux_bin():
  621.             if self.opts.reset_mbr:
  622.                 self.live.reset_mbr()
  623.             else:
  624.                 self.status(_("Warning: The Master Boot Record on your device "
  625.                               "does not match your system's syslinux MBR.  If you "
  626.                               "have trouble booting this stick, try running the "
  627.                               "liveusb-creator with the --reset-mbr option."))
  628.  
  629.         if not self.opts.partition:
  630.             try:
  631.                 self.live.mount_device()
  632.                 self._refresh_overlay_slider() # To reflect the drives free space
  633.             except LiveUSBError, e:
  634.                 self.status(e.args[0])
  635.                 self.enable_widgets(True)
  636.                 return
  637.             except OSError, e:
  638.                 self.status(_('Unable to mount device'))
  639.                 self.enable_widgets(True)
  640.                 return
  641.  
  642.         if self.opts.partition:
  643.             if not self.confirmed:
  644.                 self.status(_("Warning: All data on the selected drive will "
  645.                               "be lost."))
  646.                 self.status(_("Press 'Next' if you wish to continue."))
  647.                 self.confirmed = True
  648.                 self.startButton.setText(_("Next"))
  649.                 self.enable_widgets(True)
  650.                 return
  651.             else:
  652.                 # The user has confirmed that they wish to partition their device,
  653.                 # let's go on
  654.                 self.confirmed = False
  655.         else:
  656.             if self.live.existing_liveos():
  657.                 if not self.delete_existing_liveos_confirmed:
  658.                     self.status(_("Your device already contains a LiveOS.\nIf you "
  659.                                   "continue, this will be overwritten."))
  660.                     if self.live.existing_overlay() and self.overlaySlider.value():
  661.                         self.status(_("Warning: Creating a new persistent overlay "
  662.                                       "will delete your existing one."))
  663.                     self.status(_("Press 'Next' if you wish to continue."))
  664.                     self.delete_existing_liveos_confirmed = True
  665.                     self.startButton.setText(_("Next"))
  666.                     self.enable_widgets(True)
  667.                     return
  668.                 else:
  669.                     # The user has confirmed that they wish to overwrite their
  670.                     # existing Live OS.  Here we delete it first, in order to
  671.                     # accurately calculate progress.
  672.                     self.delete_existing_liveos_confirmed = False
  673.                     try:
  674.                         self.live.delete_liveos()
  675.                     except LiveUSBError, e:
  676.                         self.status(e.args[0])
  677.                         #self.live.unmount_device()
  678.                         self.enable_widgets(True)
  679.                         return
  680.  
  681.         # Remove the log handler, because our live thread will register its own
  682.         self.live.log.removeHandler(self.handler)
  683.  
  684.         # If we are running in clone mode, move on.
  685.         if self.live.opts.clone:
  686.             self.enable_widgets(False)
  687.             self.live_thread.start()
  688.         # If the user has selected an ISO, use it.
  689.         elif self.live.source.__class__ == LocalIsoSource:
  690.             self.enable_widgets(False)
  691.             self.live_thread.start()
  692.         # Else, download an ISO.
  693.         elif config['download']['enabled']:
  694.             self.downloader = ReleaseDownloader(
  695.                     self.downloadCombo.currentText(),
  696.                     progress=self.download_progress,
  697.                     proxies=self.live.get_proxies())
  698.             self.connect(self.downloader,
  699.                          QtCore.SIGNAL("dlcomplete(PyQt_PyObject)"),
  700.                          self.download_complete)
  701.             self.connect(self.downloader,
  702.                          QtCore.SIGNAL("status(PyQt_PyObject)"),
  703.                          self.status)
  704.             self.downloader.start()
  705.         else:
  706.             raise NotImplementedError
  707.  
  708.     def download_complete(self, iso):
  709.         """ Called by our ReleaseDownloader thread upon completion.
  710.  
  711.         Upon success, the thread passes in the filename of the downloaded
  712.         release.  If the 'iso' argument is not an existing file, then
  713.         it is assumed that the download failed and 'iso' should contain
  714.         the error message.
  715.         """
  716.         if os.path.exists(iso):
  717.             self.status(_("Download complete!"))
  718.             self.live.source = LocalIsoSource(path=iso)
  719.             self.live_thread.start()
  720.         else:
  721.             self.status(_("Download failed: " + iso))
  722.             self.status(_("You can try again to resume your download"))
  723.             self.enable_widgets(True)
  724.  
  725.     def selectfile(self, isofile=None):
  726.         if not isofile:
  727.             isofile = QtGui.QFileDialog.getOpenFileName(self,
  728.                          _("Select Live ISO"), ".", "ISO (*.iso)" )
  729.         if isofile:
  730.             if not os.access(isofile, os.R_OK):
  731.                 self.status(_("The selected file is unreadable. "
  732.                               "Please fix its permissions or select another file."))
  733.                 return False
  734.             try:
  735.                 self.live.source = LocalIsoSource(path=isofile)
  736.             except Exception, e:
  737.                 self.status(_("Unable to use the selected file.  "
  738.                               "You may have better luck if you move your ISO "
  739.                               "to the root of your drive (ie: C:\)"))
  740.                 self.live.log.exception(e.args[0])
  741.                 return False
  742.  
  743.             self.live.log.info(_("%(filename)s selected")
  744.                                  % {'filename': os.path.basename(self.live.source.path)})
  745.             self.source_available = True
  746.             self.update_start_button()
  747.             self._refresh_overlay_slider()
  748.  
  749.     def terminate(self):
  750.         """ Terminate any processes that we have spawned """
  751.         self.live.terminate()
  752.